// Go作者版权所有。版权所有。
// 此源代码的使用受BSD样式的约束
// 可以在许可证文件中找到的许可证。

// 开始：建造！网间网操作系统
// +建造！网间网操作系统

package x509

import (
	"bytes"
	macOS "crypto/x509/internal/macos"
	"fmt"
	"os"
	"strings"
)

var debugDarwinRoots = strings.Contains(os.Getenv("GODEBUG"), "x509roots=1")

func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate, err error) {
	return nil, nil
}

func loadSystemRoots() (*CertPool, error) {
	var trustedRoots []*Certificate
	untrustedRoots := make(map[string]bool)

	// macOS有三个信任域：一个用于用户添加到其帐户中的CA
	// “登录”密钥链，管理员添加到“系统”密钥链的CAs密钥链，
	// 一个用于操作系统附带的CAs。
	for _, domain := range []macOS.SecTrustSettingsDomain{
		macOS.SecTrustSettingsDomainUser,
		macOS.SecTrustSettingsDomainAdmin,
		macOS.SecTrustSettingsDomainSystem,
	} {
		certs, err := macOS.SecTrustSettingsCopyCertificates(domain)
		if err == macOS.ErrNoTrustSettings {
			continue
		} else if err != nil {
			return nil, err
		}
		defer macOS.CFRelease(certs)

		for i := 0; i < macOS.CFArrayGetCount(certs); i++ {
			c := macOS.CFArrayGetValueAtIndex(certs, i)
			cert, err := exportCertificate(c)
			if err != nil {
				if debugDarwinRoots {
					fmt.Fprintf(os.Stderr, "crypto/x509: domain %d, certificate #%d: %v\n", domain, i, err)
				}
				continue
			}

			var result macOS.SecTrustSettingsResult
			if domain == macOS.SecTrustSettingsDomainSystem {
				// 在系统域中找到的证书始终受信任。如果用户
				// 在这样的证书上配置“从不信任”，也可以在
				// 管理员或用户域，导致将其添加到不可信域。
				result = macOS.SecTrustSettingsResultTrustRoot
			} else {
				result, err = sslTrustSettingsResult(c)
				if err != nil {
					if debugDarwinRoots {
						fmt.Fprintf(os.Stderr, "crypto/x509: trust settings for %v: %v\n", cert.Subject, err)
					}
					continue
				}
				if debugDarwinRoots {
					fmt.Fprintf(os.Stderr, "crypto/x509: trust settings for %v: %d\n", cert.Subject, result)
				}
			}

			switch result {
			// 请注意结果kSecTrustSettingsResultTrustRoot之间的区别
			// 和kSecTrustSettingsResultTrustAsRoot：前者只能应用于
			// 根（自签名）证书；后者仅适用于
			// 非根证书。“
			case macOS.SecTrustSettingsResultTrustRoot:
				if isRootCertificate(cert) {
					trustedRoots = append(trustedRoots, cert)
				}
			case macOS.SecTrustSettingsResultTrustAsRoot:
				if !isRootCertificate(cert) {
					trustedRoots = append(trustedRoots, cert)
				}

			case macOS.SecTrustSettingsResultDeny:
				// 将此证书添加到不可信项中，不可信项将被减去
				// 来自受信任的资源，这样我们就不必评估策略
				// 对于系统域中的每个根，但仍应用用户和
				// 覆盖系统根目录的管理策略。
				untrustedRoots[string(cert.Raw)] = true

			case macOS.SecTrustSettingsResultUnspecified:
				// 应将具有未指定信任的证书添加到池中
				// 用于连锁建设的中间产品，但我们不支持
				// 现在。这是第35631期。

			default:
				if debugDarwinRoots {
					fmt.Fprintf(os.Stderr, "crypto/x509: unknown trust setting for %v: %d\n", cert.Subject, result)
				}
			}
		}
	}

	pool := NewCertPool()
	for _, cert := range trustedRoots {
		if !untrustedRoots[string(cert.Raw)] {
			pool.AddCert(cert)
		}
	}
	return pool, nil
}

// exportCertificate为SecCertificateRef返回*证书。
func exportCertificate(cert macOS.CFRef) (*Certificate, error) {
	data, err := macOS.SecItemExport(cert)
	if err != nil {
		return nil, err
	}
	defer macOS.CFRelease(data)
	der := macOS.CFDataToSlice(data)

	return ParseCertificate(der)
}

// isRootCertificate报告主题和发行人是否匹配。
func isRootCertificate(cert *Certificate) bool {
	return bytes.Equal(cert.RawSubject, cert.RawIssuer)
}

// sslTrustSettingsResult获取一个对象的最终kSecTrustSettingsResult值
// 用户或管理域中的证书，结合了
// SSL扇区设置策略，
// None
// 它忽略了扇区设置SKEUSAGE和KSECTRUSTSETTINGSALLOWERROR，以及
// 不支持kSecTrustSettingsDefaultRootCertSetting。
// None
// https:
func sslTrustSettingsResult(cert macOS.CFRef) (macOS.SecTrustSettingsResult, error) {
	// 在苹果的实现中，用户信任设置覆盖管理信任设置
	// （其本身会覆盖系统信任设置）。如果是SectursSettingsCopyTrustSettings
	// 查找用户信任时失败，或返回空信任设置
	// 设置，然后回退到检查管理员信任设置。
	// None
	// 请参阅Security-59306.41.2/trust/headers/SecTrustSettings.h，以了解
	// 中的信任设置覆盖和seclegacyanchorsourcecopyusage约束
	// Security-59306.41.2/trust/trustd/SecCertificateSource.c以获取具体示例
	// 苹果如何在空信任设置或非空信任设置的情况下应用覆盖
	// 成功是错误。
	trustSettings, err := macOS.SecTrustSettingsCopyTrustSettings(cert, macOS.SecTrustSettingsDomainUser)
	if err != nil || trustSettings == 0 {
		if debugDarwinRoots && err != macOS.ErrNoTrustSettings {
			fmt.Fprintf(os.Stderr, "crypto/x509: SecTrustSettingsCopyTrustSettings for SecTrustSettingsDomainUser failed: %s\n", err)
		}
		trustSettings, err = macOS.SecTrustSettingsCopyTrustSettings(cert, macOS.SecTrustSettingsDomainAdmin)
	}
	if err != nil || trustSettings == 0 {
		// 如果返回的证书既没有用户信任设置，也没有管理员信任设置
		// 从SecTrustSettingsCopyCertificates中，Apple返回kSecTrustSettingsResultInvalid，
		// 因为此方法旨在返回具有信任设置的证书。
		// 最有可能触发这种情况的是，现有的信任设置
		// 无效，无法正确分析。在这种情况下，SecTrustSettingsCopyTrustSettings
		// 返回errSecInvalidTrustSettings。现有的cgo实现返回
		// KSectrustSettingsResult在本例中未指定，主要与Apple匹配
		// 实现，因为我们不使用带有此标记的证书执行任何操作
		// 后果
		// None
		// 请参阅Security-59306.41.2/trust/trustd/SecPolicyServer.c中的secpvcGetTrustSettings结果
		if debugDarwinRoots && err != macOS.ErrNoTrustSettings {
			fmt.Fprintf(os.Stderr, "crypto/x509: SecTrustSettingsCopyTrustSettings for SecTrustSettingsDomainAdmin failed: %s\n", err)
		}
		return macOS.SecTrustSettingsResultUnspecified, nil
	}
	defer macOS.CFRelease(trustSettings)

	// “空的信任设置数组意味着使用
	// kSecTrustSettingsResultTrustRoot证书的总体信任设置。“
	if macOS.CFArrayGetCount(trustSettings) == 0 {
		return macOS.SecTrustSettingsResultTrustRoot, nil
	}

	isSSLPolicy := func(policyRef macOS.CFRef) bool {
		properties := macOS.SecPolicyCopyProperties(policyRef)
		defer macOS.CFRelease(properties)
		if v, ok := macOS.CFDictionaryGetValueIfPresent(properties, macOS.SecPolicyOid); ok {
			return macOS.CFEqual(v, macOS.CFRef(macOS.SecPolicyAppleSSL))
		}
		return false
	}

	for i := 0; i < macOS.CFArrayGetCount(trustSettings); i++ {
		tSetting := macOS.CFArrayGetValueAtIndex(trustSettings, i)

		// 首先，检查此信任设置是否约束为非SSL策略。
		if policyRef, ok := macOS.CFDictionaryGetValueIfPresent(tSetting, macOS.SecTrustSettingsPolicy); ok {
			if !isSSLPolicy(policyRef) {
				continue
			}
		}

		// 然后检查它是否仅限于主机名，而不是根。
		if _, ok := macOS.CFDictionaryGetValueIfPresent(tSetting, macOS.SecTrustSettingsPolicyString); ok {
			continue
		}

		cfNum, ok := macOS.CFDictionaryGetValueIfPresent(tSetting, macOS.SecTrustSettingsResultKey)
		// 如果此键不存在，则假定默认值为kSecTrustSettingsResultTrustRoot
		if !ok {
			return macOS.SecTrustSettingsResultTrustRoot, nil
		}
		result, err := macOS.CFNumberGetValue(cfNum)
		if err != nil {
			return 0, err
		}

		// 如果多个词典匹配，我们应该“或”它们，
		// 其语义尚不清楚。因为TrustRoot和TrustAsRoot
		// 是互斥的，Deny可能应该重写，并且无效且
		// 如果无法覆盖，请通过首先停止来近似此值
		// 信任根、信任根或拒绝。
		switch r := macOS.SecTrustSettingsResult(result); r {
		case macOS.SecTrustSettingsResultTrustRoot,
			macOS.SecTrustSettingsResultTrustAsRoot,
			macOS.SecTrustSettingsResultDeny:
			return r, nil
		}
	}

	// 如果存在信任设置，但没有与策略匹配的设置。。。
	// 医生没有告诉我们该怎么做。
	// None
	// “如果给定使用的任何词典在
	// 证书的信任设置数组满足指定的用途。”建议
	// 好像根本就没有信任设置，所以我们应该
	// 回退到管理员信任设置？TODO（golang.org/issue/38888）。

	return macOS.SecTrustSettingsResultUnspecified, nil
}
